﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;
using static System.Windows.Forms.ComboBox;

namespace ICSharpCode.WinFormsUI.Controls
{
    public class NComboBox : System.Windows.Forms.Control
    {
        private int _itemHeight = 18;
        public int ItemHeight
        {
            get { return _itemHeight; }
            set { _itemHeight = value; }
        }

        private Image _itemIcon = null;
        public Image ItemIcon
        {
            get { return _itemIcon; }
            set { _itemIcon = value; }
        }

        private ItemCollection _items;
        public ItemCollection Items
        {
            get { return _items; }
            set
            {
                _items = value;
                this.Invalidate();
            }
        }

        private object _selectedItem;
        public object SelectedItem
        {
            get
            {
                if (_selectedItem == null)
                {
                    if ( _items.Count > _selectedIndex && _selectedIndex > -1)
                    {
                        _selectedItem = _items[_selectedIndex];
                    }
                }
                return _selectedItem;
            }
            set
            {
                _selectedItem = value;
                m_ListBox.SelectedItem = _selectedItem;
            }
        }

        private int _dropDownButtonWidth = 20;
        public int DropDownButtonWidth
        {
            get { return _dropDownButtonWidth; }
            set { _dropDownButtonWidth = value; }
        }

        private bool _fouceStatus = false;
        private bool _formattingEnabled = true;
        private int _selectedIndex = -1;
        private string _selectedText = "";
        private int _dropDownWidth = 0;
        private DrawMode _drawMode = DrawMode.Normal;
        private ComboBoxStyle _dropDownStyle = ComboBoxStyle.DropDownList;
        private Color _backColor = SystemColors.Window;
        private Color _foreColor = SystemColors.ControlText;
        private Color _borderColor = SystemColors.ControlDark;
                
        private Form popupForm = null;
        private bool popupFormShowStatus = false;

        public bool FormattingEnabled
        {
            get { return _formattingEnabled; }
            set { _formattingEnabled = value; }
        }

        public event EventHandler SelectedValueChanged = null;

        public event EventHandler SelectedIndexChanged = null;

        public int DropDownWidth
        {
            get { return _dropDownWidth; }
            set
            {
                _dropDownWidth = value;
                m_ListBox.Width = _dropDownWidth;
            }
        }

        public string SelectedText
        {
            get { return _selectedText; }             
        }

        public int SelectedIndex
        {
            get { return _selectedIndex; }
            set
            {
                _selectedIndex = value;
                m_ListBox.SelectedIndex = SelectedIndex;
                M_ListBox_SelectedIndexChanged(this,new EventArgs());
            }
        }

        private string _text;

        private TextBox m_TextBox = new TextBox();
        private NListBox m_ListBox = new NListBox();

        private Rectangle dropDownRectangle = new Rectangle();

        internal NListBox ListBox
        {
            get { return m_ListBox; }
        }

        public DrawMode DrawMode
        {
            get { return _drawMode; }
            set
            {
                _drawMode = value;
                DoTextBoxBounds();
                this.Invalidate();
            }
        }

        public ComboBoxStyle DropDownStyle
        {
            get { return _dropDownStyle; }
            set
            {
                _dropDownStyle = value;
                DoTextBoxBounds();
                this.Invalidate();
            }
        }

        [EditorAttribute(typeof(DropDownItems), typeof(System.Drawing.Design.UITypeEditor))]
        public new string Text
        {
            get
            {
                if (string.IsNullOrEmpty(_text))
                {
                    if (!string.IsNullOrEmpty(SelectedText))
                        _text = SelectedText;
                    else if (SelectedItem != null)
                        _text = SelectedItem.ToString();
                    else if (SelectedIndex != -1 && Items.Count > 0)
                        _text = Items[SelectedIndex].ToString();
                }
                return _text;
            }
            set
            {
                _text = value;
                if (m_TextBox != null)
                {
                    m_TextBox.Text = _text;
                }
                this.Invalidate();
            }
        }

        public DrawMode drawMode;

        public new Color BackColor
        {
            get { return _backColor; }
            set
            {
                _backColor = value;
                m_TextBox.BackColor = _backColor;
                m_ListBox.SetTheme(NToolStripItemTheme.Name);
                base.BackColor = _backColor;
            }
        }

        public new Color ForeColor
        {
            get { return _foreColor; }
            set
            {
                _foreColor = value;
                m_TextBox.ForeColor = _foreColor;
                m_ListBox.SetTheme(NToolStripItemTheme.Name);
                base.ForeColor = _foreColor;
            }
        }

        public Color BorderColor
        {
            get { return _borderColor; }
            set { _borderColor = value; }
        }

        //返回hWnd参数所指定的窗口的设备环境。
        [System.Runtime.InteropServices.DllImport("user32.dll")]
        static extern IntPtr GetWindowDC(IntPtr hWnd);

        [System.Runtime.InteropServices.DllImport("user32.dll")]
        //函数释放设备上下文环境（DC）  
        static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);            

        public NComboBox()
        {
            this.Size = new Size(120, 30);
            this.Items = new ItemCollection(this);
            this.Font = new System.Drawing.Font(this.Font.FontFamily, 9.0f);
            this.DrawMode = DrawMode.OwnerDrawFixed;
            this.DropDownStyle = ComboBoxStyle.DropDownList;

            m_TextBox.BorderStyle = BorderStyle.None;
            this.Controls.Add(m_TextBox);

            InitializeControls();

            this.SetStyle(ControlStyles.DoubleBuffer | ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true);
            this.UpdateStyles();
        }

        private void InitializeControls()
        {
            m_ListBox.IsHoverStyle = true;
            m_ListBox.TextRender = TextRenderModel.Single;
            m_ListBox.ItemClicked += M_ListBox_ItemClicked;
            m_ListBox.SelectedIndexChanged += M_ListBox_SelectedIndexChanged;           
            m_TextBox.TextChanged += M_TextBox_TextChanged;
            m_TextBox.GotFocus += M_TextBox_GotFocus;
            m_TextBox.LostFocus += M_TextBox_LostFocus;
        }

        private void M_TextBox_LostFocus(object sender, EventArgs e)
        {
            _fouceStatus = false;
            this.Invalidate();
        }

        private void M_TextBox_GotFocus(object sender, EventArgs e)
        {
            _fouceStatus = true;
            this.Invalidate();
        }

        private void M_TextBox_TextChanged(object sender, EventArgs e)
        {
            this._text = m_TextBox.Text;
        }

        private void M_ListBox_ItemClicked(object sender, EventArgs e)
        {
            _selectedIndex = m_ListBox.SelectedIndex;
            _selectedItem = m_ListBox.SelectedItem;
            if (DropDownStyle != ComboBoxStyle.DropDownList)
                m_TextBox.Text = _selectedItem.ToString();
            else
                this.Invalidate();
            DoCloseDropDown();
        }

        private void M_ListBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (SelectedIndexChanged != null)
            {
                SelectedIndexChanged(this, new EventArgs());
            } 
        }

        private void PopupForm_LocationChanged(object sender, EventArgs e)
        {
            popupFormShowStatus = false;
            if (popupForm != null && popupForm.Visible)
                popupForm.Hide();
        }

        private void PopupForm_Closed(object sender, FormClosedEventArgs e)
        {
            if (popupForm != null)
                popupForm.Close();
        }

        private void DoTextBoxBounds()
        {
            if (DropDownStyle != ComboBoxStyle.DropDownList)
            {
                if (!m_TextBox.Visible)
                    this.m_TextBox.Visible = true;

                this.m_TextBox.SetBounds(2, (this.Height - m_TextBox.Height) / 2,
                this.Width - _dropDownButtonWidth - 4, m_TextBox.Height);
            }
            else
            {
                if (m_TextBox.Visible)
                    this.m_TextBox.Visible = false;
            }
        }

        private void DoShowDropDown()
        {
            if (popupFormShowStatus)
            {
                DoCloseDropDown();
                return;
            }

            Form ownerForm = GetTopParentForm(this);
            if (popupForm == null)
            {
                popupForm = new Form();
                popupForm.Font = ownerForm.Font;
                popupForm.ShowInTaskbar = false;
                popupForm.FormBorderStyle = FormBorderStyle.None;
                m_ListBox.Dock = DockStyle.Fill;
                popupForm.Controls.Add(m_ListBox);
            }        
           
            Point showPopupFormLocation = this.Parent.PointToScreen(this.Location);

            int PopWinHeight = m_ListBox.ItemHeight;
            if (_items != null && _items.Count > 0)
            {
                if (_items.Count > 20)
                    PopWinHeight = 20 * m_ListBox.ItemHeight;
                else
                    PopWinHeight = _items.Count * m_ListBox.ItemHeight;
            }
            PopWinHeight += m_ListBox.Padding.Top + m_ListBox.Padding.Bottom;
            popupForm.Size = new Size(this.Width, PopWinHeight);

            int top = this.Height;
            if (top + PopWinHeight > Screen.GetWorkingArea(this).Height)
            {
                top = Screen.GetWorkingArea(this).Height - this.Height;
            }

            showPopupFormLocation.Offset(0, top);

            popupForm.Show(ownerForm);
            popupForm.BringToFront();

            popupForm.Location = showPopupFormLocation;
            popupForm.Size = new Size(this.Width, PopWinHeight);
            m_ListBox.UpdateScrollBars();

            OnShowDropDown(new EventArgs());

            popupFormShowStatus = true;

        }        

        private void DoCloseDropDown()
        {
            popupFormShowStatus = false;
            popupForm.Hide();
            OnCloseDropDown(new EventArgs());
        }

        private Form GetTopParentForm(Control control)
        {
            Form ownerForm = control.FindForm();
            if (ownerForm == null && control.Parent != null)
                ownerForm = GetTopParentForm(control.Parent);
            return ownerForm;
        }

        private void DrawComboBox(Graphics g)
        {
            g.SmoothingMode = SmoothingMode.HighQuality;
            g.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;

            int iDropDownButtonWidth = _dropDownButtonWidth;
            int iDropDownButtonHeight = this.Height;

            int iDropDownButtonLocatinX = _dropDownButtonWidth;
            int iDropDownButtonLocatinY = 0;

            //下拉按钮
            dropDownRectangle = new Rectangle(ClientRectangle.Width - iDropDownButtonLocatinX, iDropDownButtonLocatinY, iDropDownButtonWidth, iDropDownButtonHeight);

            //背景色刷
            Brush bkgBrush = new SolidBrush(_backColor);

            //字体色刷
            Brush fcBrush = new SolidBrush(_foreColor);

            int iBackColorX = 0;
            //为了字体正常，Enable时只是重画按钮区域
            if (this.Enabled)
            {
                iBackColorX = this.Width - 20;
            }

            string selectedText = _text;

            if (SelectedItem != null)
            {
                selectedText = SelectedItem.ToString();
            }

            if (!Enabled)
            {
                fcBrush = new SolidBrush(Color.FromArgb(200, _foreColor));
            }

            //画背景
            //g.FillRectangle(bkgBrush, iBackColorX, 0, ClientRectangle.Width, ClientRectangle.Height);
            g.FillRectangle(bkgBrush, ClientRectangle);

            //画文本
            //g.DrawString(selectedText, this.Font, fcBrush, new Rectangle(new Point(4, 2), new Size(this.Width - iDropDownButtonWidth - 8, this.Height - 4)), new StringFormat() { LineAlignment = StringAlignment.Center });
            if (DropDownStyle == ComboBoxStyle.DropDownList && !string.IsNullOrEmpty(selectedText))
                TextRenderer.DrawText(g, selectedText, this.Font, new Rectangle(new Point(4, 2), new Size(this.Width - iDropDownButtonWidth - 8, this.Height - 4)), ((SolidBrush)fcBrush).Color, TextFormatFlags.SingleLine | TextFormatFlags.VerticalCenter | TextFormatFlags.WordEllipsis);

            //画下拉按钮
            if (DropDownStyle != ComboBoxStyle.Simple)
                ComboBoxRenderer.DrawDropDownButton(g, dropDownRectangle, this.Enabled ? ComboBoxState.Normal : ComboBoxState.Disabled);

            //画边框  
            Color _drawborderColor = _borderColor;
            if (_fouceStatus)
                _drawborderColor = Color.Orange;
            ControlPaint.DrawBorder(g, new Rectangle(0, 0, this.Width, this.Height), _drawborderColor, ButtonBorderStyle.Solid);

            //g.Dispose();

            bkgBrush.Dispose();
            fcBrush.Dispose();

        }

        protected virtual void OnShowDropDown(EventArgs e)
        {

        }

        protected virtual void OnCloseDropDown(EventArgs e)
        {

        }

        protected virtual void OnMeasureItem(MeasureItemEventArgs e)
        {
            e.ItemHeight = _itemHeight;
        }

        protected virtual void OnDrawItem(DrawItemEventArgs e)
        {
            //获取要在其上绘制项的图形表面
            Graphics g = e.Graphics;           
            System.Drawing.Rectangle rect = e.Bounds;

            Brush itemBrush = null;
            Color itemTextForeColor = Color.Transparent;

            if (e.Index <= -1)
                return;

            if (e.State == DrawItemState.None ||
                    e.State == (DrawItemState.NoAccelerator | DrawItemState.NoFocusRect))
            {
                itemBrush = new SolidBrush(_backColor);
                itemTextForeColor = _foreColor;
            }
            else if (e.State == DrawItemState.Selected ||
                e.State == DrawItemState.Focus ||
                e.State == (DrawItemState.Selected | DrawItemState.NoAccelerator | DrawItemState.NoFocusRect)||
                e.State == (DrawItemState.Selected | DrawItemState.Focus | DrawItemState.NoAccelerator | DrawItemState.NoFocusRect) ||
                e.State == (DrawItemState.Selected | DrawItemState.Focus | DrawItemState.NoAccelerator | DrawItemState.NoFocusRect | DrawItemState.ComboBoxEdit))
            {
                itemBrush = new SolidBrush(SystemColors.Highlight);
                itemTextForeColor = Color.White;
            }
            else
            {
                itemBrush = new SolidBrush(_backColor);
                itemTextForeColor = _foreColor;
            }
            g.FillRectangle(itemBrush, rect);
            int drawTextPosX = rect.Left;
            int drawTextPosY = rect.Top;
            Rectangle itemTextRect = new Rectangle(drawTextPosX, drawTextPosY, rect.Width, rect.Height);
            if (_itemIcon != null)
            {
                drawTextPosX += _itemIcon.Size.Width + 4;
                itemTextRect = new Rectangle(drawTextPosX, drawTextPosY, rect.Width - _itemIcon.Size.Width - 8, rect.Height);
                g.DrawImage(_itemIcon, new Point(rect.Left + 2, rect.Top + (rect.Height - _itemIcon.Height) / 2));
            }
            string itemText = Items[e.Index].ToString();
            TextRenderer.DrawText(g, itemText, this.Font, itemTextRect, itemTextForeColor, TextFormatFlags.SingleLine | TextFormatFlags.VerticalCenter | TextFormatFlags.WordEllipsis);
            e.DrawFocusRectangle();
        }

        protected override void OnSizeChanged(EventArgs e)
        {
            base.OnSizeChanged(e);

            if (_dropDownStyle == ComboBoxStyle.DropDownList)
                return;

            DoTextBoxBounds();
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            if (dropDownRectangle.Contains(e.Location))
            {
                if (this.Cursor != Cursors.Hand)
                    this.Cursor = Cursors.Hand;
                return;
            }
            if (this.Cursor != Cursors.Default)
                this.Cursor = Cursors.Default;
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);

            if (_dropDownStyle == ComboBoxStyle.DropDownList)
            {
                DoShowDropDown();
            }
            else if (dropDownRectangle.Contains(e.Location))
            {
                DoShowDropDown();
            }
        }

        protected override void OnGotFocus(EventArgs e)
        {
            base.OnGotFocus(e);
            //_fouceStatus = true;
            //this.Invalidate();
        }

        protected override void OnLostFocus(EventArgs e)
        {
            base.OnLostFocus(e);
            //_fouceStatus = false;
            //this.Invalidate();
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            DrawComboBox(e.Graphics);
        }

        protected override void OnCreateControl()
        {
            base.OnCreateControl();
            Form ownerForm = GetTopParentForm(this);
            if (ownerForm == null) return;
            ownerForm.LocationChanged += PopupForm_LocationChanged;
            ownerForm.FormClosed += PopupForm_Closed;
        }

        
    }
}
